#pragma once
#include <string>
#include <random>
#include <mutex>
#include <sstream>
#include <stdexcept>
#include <chrono>

/**
 *                              ***********************
 *                              *       雪花算法       *
 *                              ***********************
 *
 *使用实例：
 *SnowflakeIdTool s(1L,1L);
 * std::cout << s.getNextId() << std::endl;
 */

//1位标识符：0 41位时间戳 10位工作Id 12位序列号
class  SnowflakeIdTool {
private:

    //开始时间戳：2015-01-01
    const long long m_twepoch = 1288834974657L;

    //机器标识id所占位数
    const long long m_workerIdBits = 5L;

    //数据标识id所占的位数
    const long long m_datacenterIdBits = 5L;

    //支持最大的机器id，结果是31(这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
    const long long m_maxWorkerId = -1L^(-1L << m_workerIdBits);

    //支持的最大数据标识id，结果是31
    const long long m_maxDatacenterId = -1L^(-1L << m_datacenterIdBits);

    //序列在ID中的占的位数
    const long long m_sequenceBits = 12L;

    //机器id左移12位
    const long long m_workerIdShift = m_sequenceBits;

    //数据标识id向左移17位（12+5）
    const long long m_datacenterIdShift = m_sequenceBits+m_workerIdBits;

    //时间戳向左移动22位（12+5+5）
    const  long long m_timeStampLeftShift = m_sequenceBits+m_workerIdBits+m_datacenterIdBits;

    //生产序列的掩码，这里为4095 (0b111111111111=0xfff=4095)
    const long long m_sequenceMask = -1L^(-1L << m_sequenceBits);

    //工作机器id(0-31)
    long long m_workerId;

    //数据中心Id(0-31)
    long long m_datacenterId;

    //毫秒内序列（0-4095）
    long long m_sequence = 0L;

    //上次生成的时间戳
    long long m_lastTimeStamp = -1L;

    std::mutex m_idMutex;

private:
    /**
     * 获取毫秒级时间戳
     * @return
     */
    long long timeGen() {
        std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
        return std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
    }

    /**
     * 获取下一毫秒时间戳
     * @param lastTimeStamp
     * @return
     */
    long long tilNextMillis(long long lastTimeStamp) {
        long long timeStamp = this->timeGen();
        while (timeStamp <= lastTimeStamp) timeStamp = this->timeGen();
        return timeStamp;
    }


public:
    /**
     * 构造函数
     * @param workerId
     * @param datacenterId
     */
     SnowflakeIdTool(long long workerId, long long datacenterId) {

        if (workerId > m_maxWorkerId || workerId < 0)
            throw std::runtime_error("workerId错误");

        if (datacenterId > m_maxDatacenterId || datacenterId < 0)
            throw std::runtime_error("datacenterId错误");

        this->m_workerId = workerId;
        this->m_datacenterId = datacenterId;
    }


    ~SnowflakeIdTool() {}


    /**
     *
     * @return
     */
    long long getNextId() {

        std::unique_lock<std::mutex> lock(this->m_idMutex);

        long long timeStamp = this->timeGen();

        //如果当前时间小于上一次Id生成的时间戳，说明时钟回退过，这个时候应该抛出异常
        if (timeStamp < m_lastTimeStamp) throw std::runtime_error("时间戳错误");


        //如果是同一时间生成的，则进行毫秒内序列
        if (timeStamp == this->m_lastTimeStamp) {
            this->m_sequence = (this->m_sequence + 1) & m_sequenceMask;

            //毫秒内序列溢出
            if (this->m_sequence == 0) timeStamp = this->timeGen();
        }

        //时间戳改变，毫秒内序列重置
        else this->m_sequence = 0L;

        // 上次生成ID的时间截
        this->m_lastTimeStamp = timeStamp;

        return ((timeStamp - this->m_twepoch) << this->m_timeStampLeftShift)
                | (this->m_datacenterId << this->m_datacenterIdShift)
                | (this->m_workerId << this->m_workerIdShift)
                | this->m_sequence;
    }


};
